#include "sys_dma.h"
#include "sys_interrupt.h"
#include "sys_uart.h"
#include "sys_gpio.h"
#include "sys_delay.h"
#include "sys_io.h"
#include "sys_timer.h"
#include "sys_cache.h"
#include "sys_clock.h"

/*DMA-寄存器地址*/
#define DMA_Base_Address (0x01C02000)
#define write_update_bits(addr, bit, Clear, Value) write32(addr, read32(addr) & (~((u64_t)(Clear) << bit)) | ((u64_t)(Value) << bit))

#define CCU_Base_Address (u32_t)0x01C20000
#define CCU_BUS_CLK_GATING_REG0 (u32_t) CCU_Base_Address + 0x0060
#define CCU_BUS_CLK_GATING_REG1 (u32_t) CCU_Base_Address + 0x0064
#define CCU_BUS_CLK_GATING_REG2 (u32_t) CCU_Base_Address + 0x0068
#define CCU_BUS_SOFT_RST_REG0 (u32_t) CCU_Base_Address + 0x02C0
#define CCU_BUS_SOFT_RST_REG1 (u32_t) CCU_Base_Address + 0x02C4
#define CCU_BUS_SOFT_RST_REG2 (u32_t) CCU_Base_Address + 0x02D0

/////////////////////////////////////////////
/*DMA初始化*/
void DMA_Init(void)
{
	Open_Dev_Clock(DMA_CLOCK);
}
/*DMA退出*/
void DMA_Exit(void)
{
	Close_Dev_Clock(DMA_CLOCK);
}
/*dma enable*/
void DMA_Enable(DMA *dma)
{
	S_BIT(dma->addr + 0x0, 31);
}
/*dma disable*/
void DMA_Disable(DMA *dma)
{
	C_BIT(dma->addr + 0x0, 31);
}
/*Normal DMA dma_ch Half Transfer Interrupt Pending (n=0~3)*/
int DMA_Get_Half_TIP(DMA *dma)
{
	if (read32(DMA_Base_Address + 0x4) & ((0x1 << (2 * dma->Ch + 0)) << (dma->Type == NDMA ? 0 : 16)))
	{
		S_BIT(DMA_Base_Address + 0x4, ((2 * dma->Ch + 0) << (dma->Type == NDMA ? 0 : 16))); // 清中断
		return 1;
	}
	return 0;
}
/*Normal DMA n Full Transfer Interrupt Pending (n=0~3)*/
int DMA_Get_Full_TIP(DMA *dma)
{
	if (read32(DMA_Base_Address + 0x4) & ((0x1 << (2 * dma->Ch + 1)) << (dma->Type == NDMA ? 0 : 16)))
	{
		S_BIT(DMA_Base_Address + 0x4, ((2 * dma->Ch + 1) << (dma->Type == NDMA ? 0 : 16))); // 清中断
		return 1;
	}
	return 0;
}
/*中断使能 (n=0~3)*/
int DMA_Interrupt_Control_Half(DMA *dma, int c)
{
	if (dma->Type) // 初始化过
	{
		if (c)
			S_BIT(DMA_Base_Address + 0x0, ((2 * dma->Ch + 0) + ((dma->Type == NDMA) ? 0 : 16)));
		else
			C_BIT(DMA_Base_Address + 0x0, ((2 * dma->Ch + 0) + ((dma->Type == NDMA) ? 0 : 16)));
	}
	else
	{
		sysprintf("Init err...\r\n");
		return -1;
	}
	return 0;
}
/*中断使能 (n=0~3)*/
int DMA_Interrupt_Control_Full(DMA *dma, int c)
{
	if (dma->Type) // 初始化过
	{
		if (c)
			S_BIT(DMA_Base_Address + 0x0, ((2 * dma->Ch + 1) + ((dma->Type == NDMA) ? 0 : 16)));
		else
			C_BIT(DMA_Base_Address + 0x0, ((2 * dma->Ch + 1) + ((dma->Type == NDMA) ? 0 : 16)));
	}
	else
	{
		sysprintf("Init err...\r\n");
		return -1;
	}
	return 0;
}
/*
NDMA_Config
*/
int DMA_Config(DMA *dma)
{
	uint32_t dma_max_length = (dma->Type == NDMA ? (dma->Read_Byte_Counter_Enable ? 131072 : 262144) : 16777216);

	if (dma->Byte_Counter > dma_max_length)
	{
		sysprintf("ERR:Byte_Counter ERR %d > %d KB\r\n", dma->Byte_Counter / 1024, dma_max_length / 1024);
		return -1;
	}
	// 返回地址
	dma->addr = (DMA_Base_Address + dma->Type + dma->Ch * 0x20);

	if (dma->Continuous_Mode_Enable == 1)
	{
		S_BIT(DMA_Base_Address + 0x8, 16); // 连续模式下要设置为1
	}
	else
	{
		C_BIT(DMA_Base_Address + 0x8, 16); // 设置为0
	}
	write32(dma->addr + 0xC, dma->Byte_Counter); // 设置BCNT

	write_update_bits(dma->addr + 0x0, 29, 0x1, dma->Continuous_Mode_Enable);	// 设置DMA连续使能
	write_update_bits(dma->addr + 0x0, 15, 0x1, dma->Read_Byte_Counter_Enable); // 读计数值使能
	// 设置源
	write32(dma->addr + 0x4, (u32_t)dma->Source_Address);				  // 设置源地址
	write_update_bits(dma->addr + 0x0, 0, 0x1F, dma->Source_DRQ_Type);	  // 设置源类型
	write_update_bits(dma->addr + 0x0, 8, 0x3, dma->Source_Data_Width);	  // 设置源宽度为16位
	write_update_bits(dma->addr + 0x0, 5, 0x3, dma->Source_Address_Type); // 设置源地址模式
	write_update_bits(dma->addr + 0x0, 7, 0x1, dma->Source_Burst_Length); // 源 Burst Length
	// 设置目标
	write32(dma->addr + 0x8, (u32_t)dma->Destination_Address);					// 设置目标地址
	write_update_bits(dma->addr + 0x0, 16, 0x1F, dma->Destination_DRQ_Type);	// 设置目标类型
	write_update_bits(dma->addr + 0x0, 24, 0x3, dma->Destination_Data_Width);	// 设置目标宽度为16位
	write_update_bits(dma->addr + 0x0, 21, 0x3, dma->Destination_Address_Type); // 设置目标地址模式
	write_update_bits(dma->addr + 0x0, 23, 0x1, dma->Destination_Burst_Length); // 目标 Burst Length

	return 0;
}
